Skip to content

Conversation

@cschramm
Copy link

The parser strictly checks reserved areas to be zero (even though the specification does not even require that for all of them). This is an issue when parsing future (i.e., not known to the current codebase) versions of attestation reports, where those areas may no longer be reserved.

The lax-parser feature disables that check, currently with some tradeoffs:

  • No WriteExt::skip_bytes
  • No Encoder impl for AttestationReport, Signature, and WrappedVlekHashstick
  • No compatibility with openssl and crypto_nossl

Closes #343

The parser strictly checks reserved areas to be zero (even though the specification does not even require that for all of them). This is an issue when parsing newer versions of attestation reports where those areas might not be reserved anymore. The `lax-parser` feature disables that check, currently with some tradeoffs:

* No `WriteExt::skip_bytes`
* No `Encoder` impl for `AttestationReport`, `Signature`, and `WrappedVlekHashstick`
* No compatibility with `openssl` and `crypto_nossl`

Closes virtee#343

Signed-off-by: Christopher Schramm <[email protected]>
@DGonzalezVillal
Copy link
Member

So in this solution, you would only be able to parse the attestation report, but you would not be able to write it anywhere correct? Is that still useful to you?

@DGonzalezVillal
Copy link
Member

Because the Write stuff can be modified to write the raw contents. Create something like a raw write

@cschramm
Copy link
Author

Is that still useful to you?

Absolutely, yes. I only use the parser, but not the Write stuff.

As I wrote in #343, writing could be achieved by actually reading reserved areas and carrying their contents to write them back instead of zeroes. I was under the impression, though, that you wanted to keep this a limited parse-only case.

Copy link
Member

@DGonzalezVillal DGonzalezVillal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello @cschramm,

Apologies for the delay—I was out on break.

After taking a closer look at your PR, I’m thinking a better approach might be to shift from the current lax-parsing idea to something more like a raw read/write (or unsafe read/write) mode. With that approach, this feature would simply read and write the raw binary contents as-is.

In our earlier discussions, I may have implied that I wanted this scoped strictly to parsing, but after reviewing the PR and realizing that lax parsing would require disabling or weakening other APIs, I’m less comfortable with that direction. I’d prefer not to reduce existing functionality to support a single feature. This alternative also results in fewer changes overall, since it would mainly involve updating the read and write paths to operate on raw bytes without safety checks.

This keeps the library behavior unchanged for most users, while clearly shifting responsibility for validation and safety checks to users who opt into this mode.

Please let me know what you think, and apologies again for the back and forth.

Thanks!

Ok(())
}

#[cfg(not(feature = "lax-parser"))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you have to disable this API for lax-parser?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It encodes hashstick, which is not supported as it requires skip_bytes.

Comment on lines +93 to +96
#[cfg(all(
any(feature = "openssl", feature = "crypto_nossl"),
feature = "lax-parser"
))]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would lax-parser be part of this check?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is precisely to detect the use of one of the crypto features together with lax-parser. The only purpose is to give a clear error message above the error messages that will follow due to the disabled code.

Copy link
Author

@cschramm cschramm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @DGonzalezVillal, no worries. A lax parser that does not disable encoding support makes perfect sense to me. I'll prepare an alternative PR for that. 👍

Edit: => #366 Keeping this open for reference until the other direction is settled.

Ok(())
}

#[cfg(not(feature = "lax-parser"))]
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It encodes hashstick, which is not supported as it requires skip_bytes.

Comment on lines +93 to +96
#[cfg(all(
any(feature = "openssl", feature = "crypto_nossl"),
feature = "lax-parser"
))]
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check is precisely to detect the use of one of the crypto features together with lax-parser. The only purpose is to give a clear error message above the error messages that will follow due to the disabled code.

@hyperfinitism
Copy link
Contributor

This looks like a very useful change overall, especially treating reserved fields in the top-level AttestationReport as raw bytes and relaxing the zero-check via a new feature. That aligns better with the ABI spec and improves forward compatibility.

One subtle point: in the ABI spec, some reserved fields are explicitly marked as MBZ (must be zero), while others are simply described as reserved without an MBZ requirement. In other words, the ABI does not guarantee that all reserved fields are zero-filled. From that perspective, it would be more spec-accurate to always zero-check only explicitly documented MBZ fields, and avoid zero-checking non-MBZ reserved fields even without enabling the feature.

The same issue also applies to sub-structures (e.g. TCB Version, Guest Policy, Platform Info), which still handle reserved fields in the old way.

As @DGonzalezVillal suggested:

a better approach might be to shift from the current lax-parsing idea to something more like a raw read/write (or unsafe read/write) mode

I strongly agree with this direction. A raw read/write model avoids baking ABI assumptions into parsing, and it naturally resolves the MBZ vs non-MBZ ambiguity (including for sub-structures) in a cleaner and more scalable way.

As additional context, similar strict-parsing assumptions have already caused issues downstream. For example:

Both were ultimately related to parsing expectations around attestation report layout and reserved fields. A raw read/write (opaque binary) model would not only prevent this class of bugs, but also make root-cause analysis and fixes much simpler when ABI-level discrepancies occur.

@cschramm
Copy link
Author

@hyperfinitism

One subtle point: in the ABI spec, some reserved fields are explicitly marked as MBZ (must be zero), while others are simply described as reserved without an MBZ requirement. In other words, the ABI does not guarantee that all reserved fields are zero-filled. From that perspective, it would be more spec-accurate to always zero-check only explicitly documented MBZ fields, and avoid zero-checking non-MBZ reserved fields even without enabling the feature.

I mentioned that in #343 (comment), but @DGonzalezVillal responded with

On reserved fields: you can assume they are zero in supported versions.

I do not understand why, but I'm definitely the one with less domain knowledge here.

In case you missed it: There is #366 with an alternative approach that reads raw bytes.

@hyperfinitism
Copy link
Contributor

In case you missed it: There is #366 with an alternative approach that reads raw bytes.

Yes. I read it. The alternative design I have in mind is to store the attestation report as a 1184-byte blob while providing setters and getters for each field.

@cschramm
Copy link
Author

I see. AttestationReport's Decoder would then use those getters? How would it apply the zero checks? Raw access (applying structural knowledge)? A method on the raw type dedicated to those checks? 🤔

Fine for me either. @DGonzalezVillal?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Forward compatibility

5 participants